package com.lh.center.common.web.session;

import com.lh.center.common.base.session.SessionUser;
import com.lh.center.common.exception.CommonException;
import com.lh.center.common.exception.ErrorEnum;
import com.lh.center.common.util.LoggerUtil;
import com.lh.center.common.web.CommonWebAutoConfiguration;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

@Component
public class SessionOperation {

    public static final ThreadLocal<SessionUser> user = new ThreadLocal<>();

    //dubbo 消费者请求时的登陆信息
    public static final ThreadLocal<SessionUser> dubboConsumerUser = new ThreadLocal<>();
    //当前请求的参数信息
    public static final ThreadLocal<Object> requestParams = new ThreadLocal<>();
    //当前请求相应的参数
    public static final ThreadLocal<Object> responseParams = new ThreadLocal<>();
    //当前请求的响应数据是否需要被包装
    public static final ThreadLocal<Boolean> toPackage = new ThreadLocal<>();
    //当前请求的参数和响应数据是否需要进行AES加解密
    public static final ThreadLocal<Boolean> toSecret = new ThreadLocal<>();
    //当前请求的接口是否需要判断登陆权限
    public static final ThreadLocal<Boolean> toAuth = new ThreadLocal<>();
    //当前请求是否打印日志
    public static final ThreadLocal<Boolean> toLog = new ThreadLocal<>();
    //当前请求如果报错是否返回堆栈信息
    public static final ThreadLocal<Boolean> toTrance = new ThreadLocal<>();
    //当前请求的日志流水记录
    public static final ThreadLocal<List<SessionLogStorage>> logs = new ThreadLocal<>();

    private static CommonWebAutoConfiguration configuration;

    private static SessionUserFetch<?> sessionUserFetch;

    //token的值
    public static final ThreadLocal<String> tokenValue = new ThreadLocal<>();

    public static SessionUser getDubboUser(){
        return dubboConsumerUser.get();
    }

    /**
     * 获取当前登陆用户(不报错)
     */
    public static <T extends SessionUser>T getUserNotException(){
        SessionUser currentUser = user.get();
        if(currentUser != null){
            return (T) currentUser;
        }
        if(!configuration.getEnable() || !configuration.isEnableAuth()){
            SessionUser user = new SessionUser();
            user.setUserId(configuration.getDefaultUserId());
            return (T) user;
        }
//        //不校验权限，暂时也不从远程获取信息了
//        if(toAuth.get()==null || !toAuth.get()){
//            return (T) new SessionUser();
//        }
        //从缓存中获取
        currentUser = (T) sessionUserFetch.getUser(tokenValue.get());
        user.set(currentUser);
        return (T) currentUser;
    }

    /**
     * 获取当前登陆用户，取不到抛出未登录异常
     */
    public static <T extends SessionUser>T getUser(){
        SessionUser user = getUserNotException();
        if(user == null){
            throw new CommonException(ErrorEnum.user_not_login);
        }
        SessionOperation.user.set(user);
        return (T) user;
    }

    /**
     * 清除用户登陆信息
     */
    public static void clear(){
        user.remove();
        toAuth.remove();
        toSecret.remove();
        toPackage.remove();
        toLog.remove();
        tokenValue.remove();
        requestParams.remove();
        LoggerUtil.clearTransId();
        responseParams.remove();
        toTrance.remove();
        logs.remove();
    }

    /**
     * 刷新用户缓存
     */
    public static void refresh(){
        if(!configuration.getEnable() || !configuration.isEnableAuth()){
            return ;
        }
        String headerToken = getHeaderToken();
        if(StringUtils.isBlank(headerToken)){
            return ;
        }
        sessionUserFetch.refresh(headerToken);
    }

    /**
     * 用户请求进入
     */
    public static void request(){
        HttpServletRequest request = getRequest();
        String requestURI = request.getRequestURI();
        PathMatcher matcher = new AntPathMatcher();
        //判断是否需要日志打印
        toLog.set(configuration.getLogUri().stream().anyMatch(uri -> matcher.match(uri, requestURI)));
        //判断是否需要进行包装
        toPackage.set(configuration.getNotPackageUri().stream().noneMatch(uri -> matcher.match(uri, requestURI)));
        //判断是否需要登陆权限
        toAuth.set(configuration.getNotAuthUri().stream().noneMatch(uri -> matcher.match(uri, requestURI)));
        //判断是否需要进行加解密
        Set<String> notSecretUris = new HashSet<>();
        notSecretUris.addAll(configuration.getNotAuthUri());
        notSecretUris.addAll(configuration.getNotSecretUri());
        toSecret.set(notSecretUris.stream().noneMatch(uri -> matcher.match(uri, requestURI)));// 全部不满足会返回true
        if(!configuration.getEnable()){
            toLog.set(false);
            toPackage.set(false);
            toAuth.set(false);
            toSecret.set(false);
        }
        if(!configuration.isEnableAuth()){
            toAuth.set(false);
        }
        if(!configuration.isEnableSecret()){
            toSecret.set(false);
        }
        if(!configuration.isEnablePackage()){
            toPackage.set(false);
        }
        tokenValue.set(getHeaderToken());
        toTrance.set(configuration.isEnableErrorTrace());
        LoggerUtil.clearTransId();
    }

    public static Long getMerchantId() {
        return getUser().getMerchantId();
    }

    public static boolean requireAuth(){
        return toAuth.get()==null?false:toAuth.get();
    }

    public static boolean requirePackage(){
        return toPackage.get()==null?false:toPackage.get();
    }

    /**
     * 获取用户传递在header头中的token值
     */
    public static String getHeaderToken(){
        return getRequest().getHeader(configuration.getTokenName());
    }

    @Resource
    @Lazy
    public void setConfiguration(CommonWebAutoConfiguration configuration) {
        SessionOperation.configuration = configuration;
//        configuration.setEnableAuth(false);
//        configuration.setEnable(false);
    }

    @Resource
    @Lazy
    public void setSessionUserFetch(SessionUserFetch<?> sessionUserFetch) {
        SessionOperation.sessionUserFetch = sessionUserFetch;
    }

    public static HttpServletRequest getRequest(){
        return ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
    }

    public static HttpServletResponse getResponse(){
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
    }

    public static CommonWebAutoConfiguration getConfiguration() {
        return configuration;
    }
}
